/********************************************************************************
* Description: event handler wrapper, associates only one descriptor event
* channel is never close fd, caller ought to manage it
* Author: hsqiang
* Create Date: 2023-06-17
********************************************************************************/
#ifndef CHANNEL_HPP_
#define CHANNEL_HPP_

#include "event_callback.hpp"
#include "noncopyable.hpp"
#include <functional>
#include <atomic>
#include <memory>
#include <sys/epoll.h>
#include <unistd.h>

namespace event_framework
{
    class EventLoop;
    class Channel final : std::enable_shared_from_this<Channel>, Noncopyable
    {
        using ReadableCallback = std::function<void()>;
        using WriteableCallback = std::function<void()>;
        using ClosedCallback = std::function<void()>;
        using ExceptionCallback = std::function<void(int)>;
    public:
        explicit Channel(EventLoop& loop_, int fd_) : loop(loop_), fd(fd_) {}
        ~Channel()
        {
            DisableAll();
        }
        int Fd() const { return fd; }
        unsigned int InsterestedEvents() const { return events; }
        void SetReceivedEvents(unsigned int revts) { revents = revts; }
        bool Available() const { return enable; }
        void DisableAll() { enable = false; }
        void ConcernReadableEvent(ReadableCallback&& func)
        {
            ASSERT(func);
            events |= (EPOLLIN | EPOLLPRI);
            readableCb = std::move(func);
            Update();
        }
        void ConcernReadableEvent()
        {
            ASSERT(readableCb);
            events |= (EPOLLIN | EPOLLPRI);
            Update();
        }
        void IgnoreReadableEvent() { events &= ~(EPOLLIN | EPOLLPRI); Update(); }
        
        void ConcernWriteableEvent(WriteableCallback&& func)
        {
            ASSERT(func);
            events |= EPOLLOUT;
            writeableCb = std::move(func);
            Update();
        }
        void ConcernWriteableEvent()
        {
            ASSERT(writeableCb);
            events |= EPOLLOUT;
            Update();
        }
        void IgnoreWriteableEvent() { events &= ~EPOLLOUT; Update(); }
        
        void ConcernClosedEvent(ClosedCallback&& func)
        {
            closedCb = std::move(func);
        }

        void ConcernExceptionEvent(ExceptionCallback&& func)
        {
            exceptionCb = std::move(func);
        }
        void HandleEvents()
        {
            HandleClose();
            HandleError();
            HandleRead();
            HandleWrite();
        }
    private:
        // after you set interested events, you should call Update() to add channel to epoll events
        void Update();
        void HandleClose();
        void HandleError();
        void HandleRead();
        void HandleWrite();

        std::atomic_bool enable { true };
        int fd { -1 };      // channel will not close this fd
        EventLoop& loop;
        unsigned int events { 0 };     // interested epoll events
        unsigned int revents { 0 };    // received epoll events
        ReadableCallback readableCb;
        WriteableCallback writeableCb;
        ClosedCallback closedCb;
        ExceptionCallback exceptionCb;
    };
    using ChannelPtr = std::shared_ptr<Channel>;
}

#endif